package com.suray.basic.wcs.plc.oper.impl;

import com.suray.basic.wcs.plc.config.resolver.PlcAnnotation;
import com.suray.basic.wcs.plc.config.resolver.PlcInterceptor;
import com.suray.basic.wcs.plc.core.Plc;
import com.suray.basic.wcs.plc.enums.Direction;
import com.suray.basic.wcs.plc.oper.PlcOper;
import com.suray.basic.wcs.plc.pojo.oper.StationOper;
import com.suray.basic.wcs.plc.pojo.oper.single.TurnoverAutomaticSystemOper;
import com.suray.basic.wcs.plc.utils.PlcAddressBit;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.basic.wcs.utils.WcsFuncUtil;
import org.springframework.cglib.proxy.Enhancer;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author shaozn
 * @date 2020/1/22 - 13:43
 * @Description PLC写操作
 */
public class PlcOperImpl implements PlcOper {
    private static Map<Plc, PlcOperImpl> plcOperMap = new HashMap();
    private Plc plc;

    public Plc getPlc() {
        return plc;
    }

    protected PlcOperImpl(Plc plc) {
        this.plc = plc;
    }

    /**
     * 此方法只使用在向PLC读数据时获取PLC实例
     *
     * @param plc
     * @return
     */
    public synchronized static PlcOperImpl getPlcOper(Plc plc) {
        PlcOperImpl plcOper = plcOperMap.get(plc);
        if (plcOper == null) {
            Enhancer plcEnhancer = new Enhancer();
            plcEnhancer.setSuperclass(PlcOperImpl.class);
            plcEnhancer.setCallback(new PlcInterceptor());
            plcOper = (PlcOperImpl) plcEnhancer.create(new Class[]{Plc.class}, new Object[]{plc});
            plcOperMap.put(plc, plcOper);
        }
        return plcOper;
    }

    /**
     * 写入读取完成信号
     *
     * @param deviceNo 设备编号
     * @param complete 完成信号
     */
    @PlcAnnotation(value = "写入读取完成信号")
    @Override
    public void readComplete(int deviceNo, boolean complete) throws IOException {
        // 设备号为0才可以发送
        while(plc.systemBaseName.getDeviceNum() != 0){
            LoggerUtil.info("等待系统设备编号为0后发送读取完成信号，当前系统设备编号deviceNo=" + plc.systemBaseName.getDeviceNum(), this.getClass());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        int[] readCmd = {deviceNo, 1};
        plc.getModbusUtil().oper("写入读取完成信号", this.getClass(),
                plc.systemBaseName.getStartNo() + PlcAddressBit.DEVICE_NO, 10, readCmd);

    }

    /**
     * 写入数据合格信号
     *
     * @param deviceNo 设备编号
     * @param eligible 是否合格
     */
    @PlcAnnotation(value = "写入数据合格信号")
    @Override
    public void dataEligible(int deviceNo, boolean eligible) throws IOException {
        // 设备号为0才可以发送
        while(plc.systemBaseName.getDeviceNum() != 0){
            LoggerUtil.info("等待系统设备编号为0后写入数据合格信号，当前系统设备编号deviceNo=" + plc.systemBaseName.getDeviceNum(), this.getClass());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        int[] eligibleCmd = {deviceNo, 0, (eligible == true) ? 1 : 2};
        plc.getModbusUtil().oper("写入数据合格信号", this.getClass(),
                plc.systemBaseName.getStartNo() + PlcAddressBit.DEVICE_NO, 10, eligibleCmd);
    }

    /**
     * 单体控制工位运转
     *
     * @param stationNo 工位编号
     * @param direction 运行方向
     */
    @PlcAnnotation(value = "单体控制工位运转")
    @Override
    public void StationRunning(int stationNo, Direction direction) throws IOException {
        // 设备号为0才可以发送
        while(plc.systemBaseName.getDeviceNum() != 0){
            LoggerUtil.info("等待系统设备编号为0后单体控制工位运转，当前系统设备编号deviceNo=" + plc.systemBaseName.getDeviceNum(), this.getClass());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        int[] stationCmd = {stationNo, direction.getDirectionNum()};
        plc.getModbusUtil().oper("单体控制工位运转", this.getClass(),
                plc.systemBaseName.getStartNo() + PlcAddressBit.STATION_NO, 1000, stationCmd);
    }

    /**
     * 控制输送线起点到终点运行
     *  @param palletNo    托盘号/任务号
     * @param Start       起点工位号
     * @param destination 终点工位号
     */
    @PlcAnnotation(value = "控制输送线起点到终点运行")
    @Override
    public void conveyorTask(int palletNo, StationOper Start, StationOper destination) throws IOException {
        // 托盘码为0才可以发送
        while(plc.systemBaseName.getPalletNo() != 0){
            LoggerUtil.info("等待系统输送线任务托盘号为0后控制输送线起点到终点运行，当前系统输送线任务托盘号palletNo=" + plc.systemBaseName.getPalletNo(), this.getClass());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        int[] conveyorCmd = {palletNo, Start.getDeviceNo(), destination.getDeviceNo()};
        plc.getModbusUtil().oper("控制输送线起点到终点运行", this.getClass(),
                plc.systemBaseName.getStartNo() + PlcAddressBit.PALLET_CODE, 1000, conveyorCmd);

        int resendCount = 10;
        while(true) {
            //如果起始工位未获取到托盘编号，任务重发
            if (resendCount == 0) {
                LoggerUtil.info("达到最高重发次数，结束重发，请检测输送线是否故障", this.getClass());
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (Start.getPalletNo() != 0) {
                break;
            }

            LoggerUtil.info(Start.getDeviceName() + "未获取到托盘编号，任务重发", this.getClass());
            plc.getModbusUtil().oper("控制输送线起点到终点运行", this.getClass(),
                    plc.systemBaseName.getStartNo() + PlcAddressBit.PALLET_CODE, 100, conveyorCmd);
            resendCount--;
        }
    }

    /**
     * 单体控制自动门
     *
     * @param doorNo 自动门编号
     * @param open   开启状态
     */
    @PlcAnnotation(value = "单体控制自动门")
    @Override
    public void automaticDoor(int doorNo, boolean open) throws IOException {
        // 设备号为0才可以发送
        while(plc.systemBaseName.getAutoDoor() != 0 || plc.systemBaseName.getOpenAutoDoor() != 0){
            LoggerUtil.info("等待系统自动门编号为0，并且自动门状态是无效后单体控制自动门，当前系统自动门编号autoDoorNo=" + plc.systemBaseName.getAutoDoor(), this.getClass());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        int[] doorCmd = {doorNo, (open == true) ? 1 : 2};
        plc.getModbusUtil().oper("单体控制自动门", this.getClass(),
                plc.systemBaseName.getStartNo() + PlcAddressBit.AUTOMATIC_DOOR_NO, 10, doorCmd);
    }

    /**
     * 锁定设备
     * @param deviceNo
     * @param password
     * @param lock
     */
    @PlcAnnotation(value = "锁定设备")
    public void lockDevice(int deviceNo, int password, boolean lock) throws IOException {
        while (((TurnoverAutomaticSystemOper) plc.systemBaseName).getLockDeviceNo() != 0) {
            LoggerUtil.info("当前无法锁定、解锁, 自动门、翻转机编号不为0", this.getClass());
            WcsFuncUtil.delay(1000);
        }

        int[] data = new int[4];
        data[0] = password;
        data[1] = lock ? 1 : 2;
        data[2] = plc.systemBaseName.getDeviceNum();
        data[3] = deviceNo;
        plc.getModbusUtil().oper("锁定设备", this.getClass(), 1003, 10, data);
    }
}
